package dkbta.server.domain.client;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.LinkedBlockingQueue;

import org.apache.log4j.Logger;

import dkbta.Config;
import dkbta.server.gui.Observable;
import dkbta.server.gui.Observer;
import dkbta.server.util.exceptions.FailedToAcceptClientException;
import dkbta.server.util.exceptions.FailedToReadQueryException;
import dkbta.server.util.query.Query;
import dkbta.server.util.query.Results;


/**
 * @author Arkady Mishiev
 *
 */

public class ClientMgr /*extends Thread implements Observable*/{
/*
	private LinkedBlockingQueue<Results> m_queryInbox;
	private LinkedBlockingQueue<Query> m_queryOutbox;
	private static Logger s_logger;
	private boolean m_shouldRun;
	private ServerSocket m_queryServerSocket = null;
	private int m_queryServerPort;
	private Observer m_logsPanelObserver;
	private Socket m_clientSocket;
	private String m_leftOver;
	private PrintWriter m_out ;
	private BufferedReader m_in;
	private boolean m_isConnected;
	private int m_clientCount;

	public ClientMgr(LinkedBlockingQueue<Query> outbox,
			LinkedBlockingQueue<Results> inbox) {
		super("ClientManager");
		m_leftOver = "";
		m_queryInbox = inbox;
		m_queryOutbox = outbox;
		s_logger = Logger.getLogger(ClientMgr.class);
		m_queryServerPort = Config.getInt("CLIENTS_REGISTRY_PORT", 5000);
		m_isConnected = false;
		m_shouldRun = true;
		m_clientCount = 0;
	}

	public void run(){
		while(m_shouldRun){
			try {
				if(m_isConnected == false){
					m_clientSocket = acceptClient();
				}				
				Query query = receiveQuery();
				setClientId(query, m_clientSocket);			
				s_logger.info("Moving query to outbox");
				m_queryOutbox.put(query);
				s_logger.info("Begin to wait for results");
				Results results = m_queryInbox.take();	
				Thread.sleep(3000);
				s_logger.info("End to wait for results. Results received ok. queryId :" + results.getQueryId());
				sendResults(results);				
			} 
			catch (FailedToAcceptClientException e1) {
				m_logsPanelObserver.updateLogsPanel("Failed to accept client");
				closeConnection();
			} 
			catch (FailedToReadQueryException e1) {
				s_logger.warn("Disconnected client");
				m_logsPanelObserver.updateLogsPanel("Disconnected client");
				closeConnection();
			}
			catch (InterruptedException e) {
				s_logger.warn("Interrupted while using inbox/outbox!");
				m_logsPanelObserver.updateLogsPanel("Interrupted while using inbox/outbox!");
				closeConnection();
			}
		}
	}

	private void setClientId(Query query, Socket mClientSocket) {
		query.setClientId((++m_clientCount) + "@" + 
				mClientSocket.getInetAddress().getHostAddress() + 
				":" + mClientSocket.getPort());		
	}

	private Query receiveQuery() throws FailedToReadQueryException {
		s_logger.info("Begin to wait for query");
		String query_str;
		try {
			query_str = nextToken();
			Query query = new Query(query_str);
			s_logger.info("End to wait for query. queryId :" + query.getId() + ".");
			return query;			
		} 
		catch (IOException e) {
			m_isConnected = false;
			throw new FailedToReadQueryException();
		}		
	}		

	public String nextToken() throws IOException, FailedToReadQueryException  {
		String ans = m_leftOver;
		String toReturn = null;
		boolean readMore = true;
		final char[] charBuffer = new char[10000];
		final StringBuilder sb = new StringBuilder();
		int k ;
		while(readMore){
			k = m_in.read(charBuffer);
			if(k == -1){
				m_isConnected = false;
				throw new FailedToReadQueryException();
			}
			sb.append(charBuffer,0,k);
			ans = ans + sb.toString();
			if(ans.contains(Config.FRAME)){
				readMore = false;
				String left = ans.substring(ans.indexOf(Config.FRAME) + 
						Config.FRAME.length(),ans.length());
				m_leftOver = left;
				ans = ans.substring(0,ans.indexOf(Config.FRAME));
				toReturn = ans.replace(Config.FRAME,"");
			}        
		}
		return toReturn ;
	}	

	private void closeConnection() {
		// TODO Auto-generated method stub
		s_logger.warn("Connection with client closed");
	}

	private void sendResults(Results results) {
		s_logger.info("Begin to send results. queryId :" + results.getQueryId() + ".");
		String str = results.toString();
		m_out.print(str + Config.FRAME);
		m_out.flush();
		s_logger.info("End to send results. queryId :" + results.getQueryId() + ".");
	}

	private Socket acceptClient() throws FailedToAcceptClientException {
		if(m_queryServerSocket == null){
			try {
				m_queryServerSocket = new ServerSocket(m_queryServerPort);
				s_logger.info("Ready on port :" + m_queryServerPort);
				m_logsPanelObserver.updateLogsPanel("[ClientMgr] Ready on port :" 
						+ m_queryServerPort);
			} catch (IOException e) {
				s_logger.fatal("Could not listen on port : " + m_queryServerPort + 
				"\nClosing ...");
				m_logsPanelObserver.updateLogsPanel("[ClientMgr] could not listen on port "
						+ m_queryServerPort + "\nClosing ...");
				try {
					Thread.sleep(5000);
				} catch (InterruptedException e1) {}
				System.exit(-1);
//				throw new FailedToAcceptClientException();
			}				
		}
		m_clientSocket = null;
		try {
			m_clientSocket = m_queryServerSocket.accept();
			m_out = new PrintWriter(m_clientSocket.getOutputStream(), true);
			m_in = new BufferedReader(new InputStreamReader(m_clientSocket.getInputStream()));
			String log = "Client accepted from " + 
			m_clientSocket.getInetAddress().getCanonicalHostName() + 
			":" + m_clientSocket.getPort();
			s_logger.info(log);
			m_logsPanelObserver.updateLogsPanel(log);
		} catch (IOException e) {
			s_logger.warn("Failed to accept client!");
			m_logsPanelObserver.updateLogsPanel("Failed to accept client!");
			throw new FailedToAcceptClientException();
		}	
		m_isConnected = true;
		return m_clientSocket;
	}

	@Override
	public void addCUPanelObserver(Observer o) {
		// TODO Auto-generated method stub

	}

	@Override
	public void addLogsPanelObserver(Observer o) {
		m_logsPanelObserver = o;		
	}

	@Override
	public void addQueriesPanelObserver(Observer o) {
		// TODO Auto-generated method stub
	}	
*/
}

